
package com.niiwoo.civet.trade.service.local.intelligenceInvest;

import com.alibaba.dubbo.common.utils.CollectionUtils;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.rpc.RpcContext;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.niiwoo.activity.stage.dto.request.prize.ActivityPrizeResetStatusReqDTO;
import com.niiwoo.activity.stage.dto.request.prize.PackageInvestPrizeOccupiedReqDTO;
import com.niiwoo.activity.stage.dto.response.prize.UserPrizeResponseDTO;
import com.niiwoo.activity.stage.enums.prize.UserPrizeSuitProjectTypeEnum;
import com.niiwoo.activity.stage.service.prize.ActivityPrizeDubboService;
import com.niiwoo.civet.account.dto.request.BillIntelligenceInvestForApplyRequestDTO;
import com.niiwoo.civet.account.dto.response.AccountBaseResponse;
import com.niiwoo.civet.account.service.AccountDubboService;
import com.niiwoo.civet.account.service.AccountInvestDubboService;
import com.niiwoo.civet.account.service.NiiwooBillDubboService;
import com.niiwoo.civet.base.constant.OrgConstant;
import com.niiwoo.civet.trade.constant.RabbitConstant;
import com.niiwoo.civet.trade.dao.entity.*;
import com.niiwoo.civet.trade.dao.mapper.*;
import com.niiwoo.civet.trade.dto.IntelligenceUnfreezeDTO;
import com.niiwoo.civet.trade.dto.common.DeadlineDTO;
import com.niiwoo.civet.trade.dto.request.IntelligenceInvestDubboRequest;
import com.niiwoo.civet.trade.dto.request.intelligenceInvest.IntelligenceInvestCombinationRequestDTO;
import com.niiwoo.civet.trade.dto.request.intelligenceInvest.IntelligenceInvestDetailRequestDTO;
import com.niiwoo.civet.trade.dto.request.intelligenceInvest.IntelligenceInvestPageRequestDTO;
import com.niiwoo.civet.trade.dto.request.intelligenceInvest.SelectIntelligenceDeadlineRequestDTO;
import com.niiwoo.civet.trade.dto.response.IntelligenceInvestOrderResponseDTO;
import com.niiwoo.civet.trade.dto.response.InvestingProjectDTO;
import com.niiwoo.civet.trade.dto.response.intelligenceInvest.*;
import com.niiwoo.civet.trade.enums.*;
import com.niiwoo.civet.trade.enums.FadadaContractEnum.ContractTypeEnum;
import com.niiwoo.civet.trade.enums.FadadaContractEnum.TradeTypeEnum;
import com.niiwoo.civet.trade.enums.Intelligence.IntelligenceSwitchSourceEnum;
import com.niiwoo.civet.trade.enums.Intelligence.ProjectPackageStatusEnum;
import com.niiwoo.civet.trade.service.local.common.InvestCommonServer;
import com.niiwoo.civet.trade.service.local.configure.TradeConfigLocalService;
import com.niiwoo.civet.trade.service.local.contract.FadadaContractContext;
import com.niiwoo.civet.trade.service.local.contract.IntelligenceRiskPrompt;
import com.niiwoo.civet.trade.service.local.contract.IntelligentServiceProtocol;
import com.niiwoo.civet.trade.service.local.invest.InvestService;
import com.niiwoo.civet.trade.utils.ProjectUtil;
import com.niiwoo.civet.user.dto.request.SimpleOrgInfo;
import com.niiwoo.civet.user.dto.request.UserLetterRequestDTO;
import com.niiwoo.civet.user.dto.response.UserBasicInfoDTO;
import com.niiwoo.civet.user.dto.response.UserIdentityInfoResponseDTO;
import com.niiwoo.civet.user.enums.MessageTemplateKeyEnum;
import com.niiwoo.civet.user.enums.MessageTemplateKeyEnum.JPushTemplateKeyEnum;
import com.niiwoo.civet.user.enums.MessageTemplateKeyEnum.LetterTemplateKeyEnum;
import com.niiwoo.civet.user.enums.SMSTemplateKeyEnum;
import com.niiwoo.civet.user.enums.UserLetterTypeEnum;
import com.niiwoo.civet.user.service.*;
import com.niiwoo.tripod.base.component.PrivacyMasks;
import com.niiwoo.tripod.base.enums.UserTypeEnum;
import com.niiwoo.tripod.base.plan.RepaymentPlanContext;
import com.niiwoo.tripod.base.plan.support.PeriodUnitEnum;
import com.niiwoo.tripod.base.plan.support.RepaymentPlanDTO;
import com.niiwoo.tripod.base.plan.support.RepaymentTypeEnum;
import com.niiwoo.tripod.lanmao.enums.UserRoleEnum;
import com.niiwoo.tripod.provider.dto.response.PageResponseDTO;
import com.niiwoo.tripod.provider.exception.BizException;
import com.niiwoo.tripod.service.component.SnowflakeIdWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import static org.springframework.transaction.annotation.Isolation.READ_COMMITTED;

@Service
@Slf4j
public class IntelligenceInvestService {
	@Reference(version = "1.0.0")
	private AccountDubboService accountDubboService;
	@Reference(version = "1.0.0")
	private AccountInvestDubboService accountInvestDubboService;
	@Reference(version = "1.0.0")
	private ActivityPrizeDubboService activityPrizeDubboService;
	@Reference(version = "1.0.0")
	private OrganizationDubboService organizationDubboService;
	@Reference(version = "1.0.0")
	private UserBasicInfoDubboService userBasicInfoDubboService;
	@Reference(version = "1.0.0")
	private UserIdentityDubboService userIdentityDubboService;
	@Reference(version = "1.0.0")
	private NiiwooBillDubboService niiwooBillDubboService;
	@Reference(version = "1.0.0")
	private UserLetterDubboService userLetterDubboService;
	@Reference(version = "1.0.0")
	private SendSmsDubboService sendSmsDubboService;
	@Autowired
	private TradeConfigLocalService tradeConfigLocalService;
	@Autowired
	private NoviceIntelligenceInvestService noviceIntelligenceInvestService;
	@Autowired
	private IntelligenceInvestFlowService intelligenceInvestFlowService;
	@Autowired
	private IntelligenceOrderService intelligenceOrderService;

	@Autowired
	private ProjectPackageMapperExt projectPackageMapperExt;
	@Autowired
	private ProjectPackageDetailMapperExt projectPackageDetailMapperExt;

	@Autowired
	private ProjectMapperExt projectMapperExt;

	@Autowired
	private InvestService investService;
	@Autowired
	private InvestCommonServer investCommonServer;
	@Autowired
	private RabbitTemplate rabbitTemplate;
	@Autowired
	private FadadaContractContext fadadaContractContext;
	@Autowired
	private ProjectContractMapperExt projectContractMapperExt;
	@Autowired
	private InvestAddInterestRateConfigMapperExt investAddInterestRateConfigMapperExt;

	@Autowired
	private SnowflakeIdWorker snowflakeIdWorker;
	@Autowired
	private ProjectInvestingMapperExt projectInvestingMapperExt;
	@Autowired
	private ProjectPackageInvestOrderMapperExt projectPackageInvestOrderMapperExt;
	@Autowired
	private ProjectPackageInvestOrderDetailMapperExt projectPackageInvestOrderDetailMapperExt;
	@Autowired
	private ProjectInvestOrderMapperExt projectInvestOrderMapperExt;
	@Autowired
	private ProjectInvestPrizeMapperExt projectInvestPrizeMapperExt;
	@Autowired
	private InvestAmountUnfreezeMapperExt investAmountUnfreezeMapperExt;
	@Autowired
	private ProjectInvestOrderProcessMapperExt projectInvestOrderProcessMapperExt;
	@Autowired
	private IntelligenceRiskPrompt intelligenceRiskPrompt;
	@Autowired
	private IntelligentServiceProtocol intelligentServiceProtocol;
	@Autowired
	private PrivacyMasks privacyMasks;
	@Value("${h5-risk-warning-url}")
	private String riskWarning;
	@Value("${h5-common-problem-url}")
	private String commonProblem;
	@Value("${h5-guarantee-model-url}")
	private String guaranteeModelH5Url;
	// 极速借个人
	@Value("${h5-fastloan-contract-url}")
	private String h5FastloanContractUrl;
	@Value("${h5-intelligenceInvest-agreement-url}")
	private String intelligenceInvestAgreementUrl;
	@Value("${h5-intelligenceInvest-risk-url}")
	private String intelligenceInvestRiskUrl;
	@Value("${h5-intelligenceInvest-common-problem-url}")
	private String intelligenceInvestCommonUrl;
	@Value("${h5-niiwoo-guarantee-model-url}")
	private String guaranteeModelUrl;

	/**
	 * 智能投开关
	 *
	 * @return
	 */
	public IntelligenceInvestSwitchResponseDTO intelligenceInvestSwitch(Byte sourceCode) {
		IntelligenceInvestSwitchResponseDTO responseDTO = new IntelligenceInvestSwitchResponseDTO();
		TradeConfigEnum config = TradeConfigEnum.SMART_INVESTMENT_SHOW_SWITCH;
		if(IntelligenceSwitchSourceEnum.SALARY.getCode().equals(sourceCode)) {
			config=TradeConfigEnum.SALARY_BAO_INVESTMENT_SHOW_SWITCH;
		}else if(IntelligenceSwitchSourceEnum.BIG_V_CUSTOMER.getCode().equals(sourceCode)) {
			config = TradeConfigEnum.BIG_V_INVESTMENT_SHOW_SWITCH;
		}
		String configValue = tradeConfigLocalService.selectTradeConfigByEmun(config);
		responseDTO.setIntelligenceInvestSwitch(configValue != null ? Byte.parseByte(configValue) : Byte.valueOf("0"));
		return responseDTO;
	}

	/**
	 * 判断智能投是否可用
	 * 
	 * @return
	 */
	public boolean intelligenceInvestUsable() {
		IntelligenceInvestSwitchResponseDTO responseDTO = this.intelligenceInvestSwitch(IntelligenceSwitchSourceEnum.NORMAL.getCode());
		if (responseDTO == null) {
			return false;
		}
		return Byte.valueOf("1").equals(responseDTO.getIntelligenceInvestSwitch());
	}

	/**
	 * 分页查询智能投
	 *
	 * @param requestDTO
	 * @return
	 */
	public PageResponseDTO<IntelligenceInvestResponseDTO> getIntelligenceInvestList(IntelligenceInvestPageRequestDTO requestDTO) {
		PageResponseDTO<IntelligenceInvestResponseDTO> pageResponseDTO = new PageResponseDTO<>();
		// 可投状态下
		List<ProjectPackage> list = projectPackageMapperExt.getIntelligenceInvestList(requestDTO);

		List<ProjectPackage> newListInvestAble = new ArrayList<>();
		if (!CollectionUtils.isEmpty(list)) {
			for (ProjectPackage projectPackage : list) {
				newListInvestAble.add(projectPackage);
			}
		}
		// 不可投状态下
		List<ProjectPackage> listUnInvest = projectPackageMapperExt.getIntelligenceUnInvestList(requestDTO);
		if (!CollectionUtils.isEmpty(listUnInvest)) {
			for (ProjectPackage projectPackage : listUnInvest) {
				newListInvestAble.add(projectPackage);
			}
			if (!CollectionUtils.isEmpty(list)) {
				for (ProjectPackage projectPackage : list) {
					for (ProjectPackage unPackage : listUnInvest) {
						if (projectPackage.getDeadlineUnit().equals(unPackage.getDeadlineUnit()) && projectPackage.getDeadline().equals(unPackage.getDeadline())
								&& projectPackage.getPackageType().equals(unPackage.getPackageType())) {
							newListInvestAble.remove(unPackage);
						}
					}
				}
			}
		}
		if (CollectionUtils.isEmpty(newListInvestAble)) {
			return pageResponseDTO;
		}
		if (requestDTO.getOffset() > newListInvestAble.size()) {
			return pageResponseDTO;
		}
		pageResponseDTO.setItems(getDtoList(newListInvestAble, requestDTO.getPageSize(), requestDTO.getOffset()));
		pageResponseDTO.measureTotalPage(newListInvestAble.size(), requestDTO.getPageSize());
		return pageResponseDTO;
	}

	private List<IntelligenceInvestResponseDTO> getDtoList(List<ProjectPackage> listAll, int pageSize, int offset) {
		List<IntelligenceInvestResponseDTO> dtoList = new ArrayList<>();
		List<ProjectPackage> list;
		if (listAll.size() < pageSize + offset) {
			list = listAll.subList(offset, listAll.size());
		} else {
			list = listAll.subList(offset, pageSize + offset);
		}

		for (ProjectPackage projectPackage : list) {
			IntelligenceInvestResponseDTO dto = this.convertToIntelligenceInvestDTO(projectPackage);
			dtoList.add(dto);
		}
		return dtoList;
	}

	private IntelligenceInvestResponseDTO convertToIntelligenceInvestDTO(ProjectPackage projectPackage) {
		IntelligenceInvestResponseDTO dto = new IntelligenceInvestResponseDTO();
		dto.setActivityTitle(projectPackage.getActivityLabel());
		dto.setBorrowRate(projectPackage.getBorrowRate());
		dto.setDeadline(projectPackage.getDeadline());
		dto.setDeadlineUnit(projectPackage.getDeadlineUnit());
		dto.setIncreaseInterest(projectPackage.getActivityRaiseRate() != null ? projectPackage.getActivityRaiseRate().multiply(BigDecimal.valueOf(100)) : null);
		dto.setInvestingAmount(projectPackage.getAmount().subtract(projectPackage.getInvestedAmount()));
		dto.setPackageId(projectPackage.getPackageId());
		dto.setPackageType(projectPackage.getPackageType());
		// 标的状态：1可投，2已抢光
		Integer packageStatus = getPackageStatus(projectPackage);
		dto.setProjectStatus(packageStatus);
		dto.setRepaymentType(projectPackage.getRepaymentType().byteValue());
		dto.setTitle(projectPackage.getTitle());
		dto.setContractAmount(projectPackage.getAmount());
		return dto;
	}

	public IntelligenceInvestDetailResponseDTO getIntelligenceInvestDetail(IntelligenceInvestDetailRequestDTO requestDTO) {
		Long packageId = requestDTO.getPackageId();
		String userId = requestDTO.getUserId();
		IntelligenceInvestDetailResponseDTO dto = new IntelligenceInvestDetailResponseDTO();
		ProjectPackage projectPackage = projectPackageMapperExt.selectByPrimaryKey(packageId);
		List<ProjectPackageInvestOrder> orderList = projectPackageInvestOrderMapperExt.selectTotalNumSuccessInvest(packageId);
		List<Long> projectIds = projectPackageDetailMapperExt.selectProjectIdByPackageId(packageId);
		List<Project> projectList = projectMapperExt.queryProjectListByProjectIds(projectIds);
		log.info("包的详情###包ID：packageId={}", packageId);
		dto.setActivityAddInerest(projectPackage.getActivityRaiseRate());
		dto.setActivityTitle(projectPackage.getActivityLabel());
		dto.setTheBorrower(Byte.valueOf("0"));// 是否借款人(0:否 1：是)
		dto.setAdditionalInvestment(Byte.valueOf("0"));// 是否追加投资(0:否 1：是)
		dto.setProjectNum(projectIds.size());
		dto.setTitle(projectPackage.getTitle());
		if (userId != null) {
			boolean isBorrower = false;
			for (Project project : projectList) {
				if (project.getBorrowUserId().equals(userId)) {
					dto.setTheBorrower(Byte.valueOf("1"));// 是否借款人(0:否 1：是)
					dto.setAdditionalInvestment(Byte.valueOf("0"));// 是否追加投资(0:否 1：是)
					isBorrower = true;
					break;
				}
			}
			if (!isBorrower) {
				for (ProjectPackageInvestOrder packageInvestOrder : orderList) {
					if (userId.equals(packageInvestOrder.getUserId())) {
						dto.setTheBorrower(Byte.valueOf("0"));// 是否借款人(0:否 1：是)
						dto.setAdditionalInvestment(Byte.valueOf("1"));// 是否追加投资(0:否 1：是)
						break;
					}
				}
			}
		}
		dto.setBeUsableCashbackPrize(projectPackage.getBeUsableCashbackPrize());
		dto.setBeUsableRaiseInterestPrize(projectPackage.getBeUsableRaiseInterestPrize());
		dto.setAdvanceOverdueDays(projectList.get(0).getAdvanceOverdueDays());
		dto.setCommonProblem(intelligenceInvestCommonUrl);
		dto.setContractAmount(projectPackage.getAmount());
		dto.setDeadline(projectPackage.getDeadline());
		dto.setDeadlineUnit(projectPackage.getDeadlineUnit());
		/*
		 * String guaranteeModelH5Url = this.guaranteeModelH5Url +
		 * "?advanceOverdueDays=" + (dto.getAdvanceOverdueDays() == null ? 0 :
		 * dto.getAdvanceOverdueDays());
		 * dto.setGuaranteeModelH5Url(guaranteeModelH5Url);
		 */
		dto.setGuaranteeModelH5Url(guaranteeModelUrl);
		dto.setIntelligenceInvest(intelligenceInvestAgreementUrl);
		dto.setInvestIncrementAmount(projectPackage.getInvestIncrementAmount());
		dto.setInvestmentRecord(orderList == null ? 0 : orderList.size());
		dto.setLoanContractInfo(h5FastloanContractUrl);
		dto.setMinInvestAmount(projectPackage.getMinInvestAmount());

		dto.setPackageId(projectPackage.getPackageId());
		// 标的状态：1可投，2已抢光
		Integer packageStatus = getPackageStatus(projectPackage);
		dto.setProjectStatus(packageStatus);
		dto.setRemainingAmount(projectPackage.getAmount().subtract(projectPackage.getInvestedAmount()));
		dto.setRepaymentWay(projectPackage.getRepaymentType().byteValue());
		dto.setRiskWarning(intelligenceInvestRiskUrl);

		dto.setYearInterestRate(projectPackage.getBorrowRate());
		dto.setPackageType(projectPackage.getPackageType());
		Long time = projectPackage.getInvestEndTime().getTime() - new Date().getTime();
		// 到时候检查 是否有状态枚举 status
		if (packageStatus == 1) {
			dto.setRemainingTime(time < 0 ? 0 : time);
		}
		log.info("智能投标的详情为:{}", JSON.toJSONString(dto));
		return dto;
	}

	
	

	private Integer getPackageStatus(ProjectPackage projectPackage) {
		Integer packageStatus;
		if (Byte.valueOf("2").equals(projectPackage.getStatus())) {
			packageStatus = 1;
		} else if (projectPackage.getAmount().compareTo(projectPackage.getInvestedAmount()) == 0) {
			packageStatus = 2;
		} else {
			packageStatus = 2;
		}
		return packageStatus;
	}

	@Transactional(isolation = Isolation.READ_COMMITTED)
	public BigDecimal selectInvestMoneyTotalForMaster(String userId) {
		return projectPackageInvestOrderMapperExt.selectInvestMoneyTotalNovice(userId);
	}

	public ProjectPackage getProjectPackageByProjectId(Long projectId) {
		Long packageId = projectPackageDetailMapperExt.selectPackageIdByProjectId(projectId);
		if (packageId == null) {
			return null;
		}
		return projectPackageMapperExt.selectByPrimaryKey(packageId);
	}

	public PageResponseDTO<InvestingProjectDTO> getIntelligenceInvestTotalProjectInfos(IntelligenceInvestCombinationRequestDTO dto) {
		PageResponseDTO<InvestingProjectDTO> page = new PageResponseDTO<>();
		List<InvestingProjectDTO> dtoList = new ArrayList<>();
		List<Long> longs = projectPackageDetailMapperExt.selectProjectIdsPage(dto);
		if (CollectionUtils.isEmpty(longs)) {
			return page;
		}
		Integer count = projectPackageDetailMapperExt.selectProjectIdsCount(dto.getPackageId());
		List<Project> projectList = projectMapperExt.selectListByProjectIdSet(longs);
		log.info("智能投包{}下面标的数量为{}个", dto.getPackageId(), projectList.size());

		// 实时找到用户信息列表
		Set<String> stringList = projectList.stream().map(Project::getBorrowUserId).collect(Collectors.toSet());
		List<UserBasicInfoDTO> userBasicInfoDTOList = userBasicInfoDubboService.queryListByUserIdSet(stringList);
		// 查询加息的标
		List<InvestAddInterestRateConfig> configs = investAddInterestRateConfigMapperExt.selectAll();
		for (Project project : projectList) {
			for (UserBasicInfoDTO userBasicInfoDTO : userBasicInfoDTOList) {
				if (userBasicInfoDTO.getUserId().equals(project.getBorrowUserId())) {
					project.setBorrowNickName(userBasicInfoDTO.getNickName());
					project.setBorrowHeadImage(userBasicInfoDTO.getHeadImage());
					break;
				}
			}
			InvestingProjectDTO investingProjectDTO = this.convertToInvestDTO(project);
			// 找到加息的活动
			for (InvestAddInterestRateConfig config : configs) {
				if (config.getId().equals(project.getActivityId())) {
					investingProjectDTO.setInterestAddTitle(config.getTitle());
					investingProjectDTO.setInterestAddRate(project.getActivityRaiseRate().multiply(BigDecimal.valueOf(100)));
					break;
				}
			}
			dtoList.add(investingProjectDTO);
		}
		// 成功借款次数
		investService.completeLoanTimes(dtoList);
		page.setItems(dtoList);
		page.measureTotalPage(count, dto.getPageSize());
		return page;
	}

	/**
	 * 持久层对象转为DTO
	 *
	 * @param project
	 * @return
	 */
	private InvestingProjectDTO convertToInvestDTO(Project project) {
		InvestingProjectDTO dto = new InvestingProjectDTO();
		dto.setProjectId(project.getProjectId());
		dto.setProjectType(project.getType());
		dto.setBorrowerUserId(project.getBorrowUserId());
		dto.setBorrowerHeadImage(project.getBorrowHeadImage());
		dto.setBorrowerNickName(project.getBorrowNickName());
		dto.setBorrowerGender(project.getBorrowGender());
		dto.setBorrowerNiiwooScore(project.getBorrowNiiwooScore());
		dto.setCity(project.getLoanCity());
		dto.setArea(project.getLoanArea());
		dto.setPublishTime(ProjectUtil.getProjectPublishtime(project.getPublishTime()));
		dto.setTitle(project.getTitle().replaceAll("借款用途：", "").replaceAll("借款用于", ""));
		dto.setBorrowRate(project.getBorrowRate());
		dto.setDeadline(project.getDeadline());
		dto.setDeadlineUnit(project.getDeadlineUnit());
		dto.setContractAmount(project.getContractAmount());
		dto.setInvestingAmount(project.getContractAmount().subtract(project.getInvestedAmount()));
		dto.setRepaymentType(project.getRepaymentType());
		dto.setBaseInvestMoney(new BigDecimal("100"));
		// 标的状态：1可投，2已抢光
		Integer projectStatus = null;
		if (Byte.valueOf("3").equals(project.getStatus()) || Byte.valueOf("5").equals(project.getStatus())) {
			projectStatus = 2;
		} else if (project.getContractAmount().compareTo(project.getInvestedAmount()) == 0) {
			projectStatus = 2;
		} else {
			projectStatus = 1;
		}
		dto.setProjectStatus(projectStatus);
		dto.setCreditType(project.getCreditType());
		dto.setBorrowerType(project.getBorrowerType());
		// 鸿特机构标的，若为“共借”借款，散标列表、标的详情页和投资订单页的授信类型不显示 (是否主借人?0.否;1.是)
		dto.setLoanType(project.getLoanType()); // 借款类型
		dto.setLoanTypeDesc(LoanTypeEnum.enumOf(project.getLoanType()).getDesc());
		dto.setIsMasterBorrower(project.getIsMasterBorrower());
		return dto;
	}

	public ProjectPackageResponseDTO getIntelligenceInvestInfo(Long packageId) {
		ProjectPackageResponseDTO projectPackageResponseDTO = new ProjectPackageResponseDTO();
		ProjectPackage projectPackage = projectPackageMapperExt.selectByPrimaryKey(packageId);
		if (projectPackage == null) {
			log.info("智能投查询packageId：{}查询无结果", packageId);
			return null;
		}
		BeanUtils.copyProperties(projectPackage, projectPackageResponseDTO);
		return projectPackageResponseDTO;
	}

	/**
	 * 智能投投资确认
	 * 
	 * @param request
	 * @return
	 */
	@Transactional(isolation = READ_COMMITTED)
	public IntelligenceInvestOrderResponseDTO intelligenceInvest(IntelligenceInvestDubboRequest request) {
		String userId = request.getUserId();
		AccountBaseResponse accountBaseNoMaster = null;
		if (UserTypeEnum.ORGANIZATION.getValue().equals(request.getInvestUserType())) {
			accountBaseNoMaster = accountDubboService.getOrgAccount(userId, userId);
		} else {
			accountBaseNoMaster = accountDubboService.loadNiiwooAccount(userId, UserRoleEnum.INVESTOR);
		}
		if (accountBaseNoMaster == null || 0 == accountBaseNoMaster.getImportUserActiva()) {
			// 未开通存管户未激活存管
			throw new BizException("TRD10034");
		}
		if (Byte.valueOf("0").equals(accountBaseNoMaster.getTender())) {
			// 智能投只支持免密投资
			throw new BizException("TRD10035");
		}
		// 查主库
		AccountBaseResponse accountBase = accountDubboService.loadAccountByIdFromMaster(accountBaseNoMaster.getAccountId());
		Byte investUserType = request.getInvestUserType();
		Long projectPackageId = request.getProjectPackageId();
		BigDecimal investAmount = request.getInvestAmount();
		// 法大大授权
		investCommonServer.fadadaGrantAuthorization(investUserType, userId);
		// 投资前验证
		investCommonServer.investCommValidate(userId, accountBase.getAvailableAmount(), investAmount, Integer.MAX_VALUE);
		// 生成投资订单 修改投资中标数据以及标数据
		ProjectPackage projectPackage = projectPackageMapperExt.selectByPrimaryKeyForUpdate(projectPackageId);
		long time = System.currentTimeMillis();
		if (!ProjectPackageStatusEnum.INVESTING.getStatus().equals(projectPackage.getStatus()) || time > projectPackage.getInvestEndTime().getTime()) {
			// 当前智能投标状态已变更，请刷新后重试
			throw new BizException("TRD10045");
		}
		// 智能投验证投资资金
		validateIntelligenceInvestAmount(investAmount, projectPackage);
		// 新手智能投 验证新手身份
		if (Byte.valueOf("2").equals(projectPackage.getPackageType())) {
			validNovice(investAmount, userId);
		}
		// 红包信息
		UserPrizeResponseDTO userPrizeResponseDTO = getAndvalidateIntelligenceInvestPrize(request);
		// 先生成智能投订单
		ProjectPackageInvestOrder investOrder = intelligenceOrderService.generatePackageInvestOrder(request, projectPackage);
		// 注册事物事件
		registerAfterCompletion(investOrder);

		Long packageInvestOrderId = investOrder.getId();
		try {
			// 冻结可用金额=投资金额，得到正确应答在继续，否则退出 后续处理异常的时候需要调用解冻
			BillIntelligenceInvestForApplyRequestDTO amountRequest = new BillIntelligenceInvestForApplyRequestDTO();
			amountRequest.setAccountId(accountBase.getAccountId());
			amountRequest.setAmount(investAmount);
			amountRequest.setOrderId(packageInvestOrderId);
			amountRequest.setProjectPackageId(projectPackageId);
			niiwooBillDubboService.billIntelligenceInvestForApply(amountRequest);
		} catch (Exception e) {
			log.warn("智能投发起资金冻结失败，订单号{}，用户ID{}，异常{}", packageInvestOrderId, userId, e.getMessage());
			unfreezeAmount(packageInvestOrderId, userId, investAmount);
			throw new BizException("TRD00000");
		}
		List<Long> projectIds = projectPackageDetailMapperExt.selectProjectIdByPackageId(projectPackageId);
		// 获取包下面还可以投资的标，按照可投金额升序
		List<ProjectInvesting> investing = projectInvestingMapperExt.selectCanInvestByProjectIds(projectIds, userId);
		IntelligenceInvestOrderResponseDTO result = new IntelligenceInvestOrderResponseDTO();
		List<ProjectInvestOrder> investOrders = null;
		try {
			investOrders = generateIntelligenceInvestOrder(investOrder, projectPackage, investing, request, userPrizeResponseDTO, accountBase);
		} catch (Exception e) {
			log.warn("智能投生成投资订单失败{},用户ID{},{}", packageInvestOrderId, userId, e.getMessage());
			unfreezeAmount(packageInvestOrderId, userId, investAmount);
			throw e;
		}
		BigDecimal interest = BigDecimal.ZERO;
		for (ProjectInvestOrder projectInvestOrder : investOrders) {
			interest = interest.add(projectInvestOrder.getInterest());
			// 产品确认 夏永确认不需要累加加息券
			// interest =
			// interest.add(Objects.nonNull(projectInvestOrder.getAddInterest())?projectInvestOrder.getAddInterest():BigDecimal.ZERO);
		}
		result.setInterest(interest);
		result.setProjectPackageInvestOrder(packageInvestOrderId);

		projectPackage.setInvestedAmount(projectPackage.getInvestedAmount().add(investAmount));
		projectPackage.setUpdateTime(new Date());
		if (projectPackage.getAmount().compareTo(projectPackage.getInvestedAmount()) == 0) {
			projectPackage.setStatus(ProjectPackageStatusEnum.PRESALE_OUT.getStatus());
		}
		projectPackageMapperExt.updateByPrimaryKeySelective(projectPackage);
		return result;
	}

	/**
	 * 注册成功或者失败事件
	 * 
	 * @param investOrder
	 */
	private void registerAfterCompletion(ProjectPackageInvestOrder investOrder) {
		// 调用智能投订单
		TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
			@Override
			public void afterCompletion(int status) {
				if (status == TransactionSynchronizationAdapter.STATUS_COMMITTED) {
					RpcContext.getContext().asyncCall(() -> accountInvestDubboService.intelligenceInvest(investOrder.getId()));
					// 智能投合同MQ 涉及 风险提示书 智能投合同 智能投风险书
					rabbitTemplate.convertAndSend(RabbitConstant.Exchange.INTELLIGENCE_INVEST_CONTRACT, RabbitConstant.RoutingKey.INTELLIGENCE_INVEST_CONTRACT, investOrder.getId());
					// 消息 通知
					DecimalFormat df = new DecimalFormat("#,##0.00");
					UserLetterRequestDTO req = new UserLetterRequestDTO();
					req.setToUserId(investOrder.getUserId());
					req.setUserLetterTypeEnum(UserLetterTypeEnum.LETTER_AND_JPUSH); // 通知+推送
					req.setLetterTemplateEnum(MessageTemplateKeyEnum.LetterTemplateKeyEnum.INTELLIGENCE_INVEST_ORDER_SUCCESS);
					req.setJpushTemplateKeyEnum(MessageTemplateKeyEnum.JPushTemplateKeyEnum.INTELLIGENCE_INVEST_ORDER_SUCCESS);
					req.setLetterTemplateParamList(Lists.newArrayList(new SimpleDateFormat("yyyy年MM月dd日 HH:mm").format(investOrder.getCreateTime()), df.format(investOrder.getAmount()),
							investOrder.getTitle(), investOrder.getDeadline()));
					req.setJpushTemplateParamList(Lists.newArrayList(new SimpleDateFormat("yyyy年MM月dd日 HH:mm").format(investOrder.getCreateTime()), df.format(investOrder.getAmount()),
							investOrder.getTitle(), investOrder.getDeadline()));
					RpcContext.getContext().asyncCall(() -> userLetterDubboService.pushLetter(req));
				} else {
					//出现失败，无条件解冻资金
					unfreezeAmount(investOrder.getId(), investOrder.getUserId(), investOrder.getAmount());
					intelligenceOrderService.changeOrderFalse(investOrder);
					
				}

			}
		});

	}

	/**
	 * 智能投验证新手
	 * 
	 * @param investAmount
	 * @param userId
	 */
	private void validNovice(BigDecimal investAmount, String userId) {
		NoviceIntelligenceInvestInfoResponseDTO dto = noviceIntelligenceInvestService.getNoviceBasicInfo(userId);
		// 过了新手期
		if (0 == dto.getNoviceSurplusDays()) {
			throw new BizException("TRD10030");
		}
		// 新手可投份额不足
		if (dto.getNoviceSurplusInvestMoney().compareTo(investAmount) < 0) {
			throw new BizException("TRD10031");
		}
	}

	/**
	 * 智能投标解冻处理
	 * 
	 * @param packageInvestOrderId
	 * @param userId
	 */
	private void unfreezeAmount(Long packageInvestOrderId, String userId, BigDecimal amount) {
		try {
			niiwooBillDubboService.billIntelligenceInvestCancel(userId, packageInvestOrderId + "");
		} catch (Exception e) {
			IntelligenceUnfreezeDTO dto = new IntelligenceUnfreezeDTO();
			dto.setAmount(amount);
			dto.setProjectPackageInvestOrderId(packageInvestOrderId);
			dto.setUserId(userId);
			intelligenceOrderService.unfreezeAmountInsert(dto);
			log.warn("智能投资金解冻失败{},用户ID{},{}", packageInvestOrderId, userId, e.getMessage());
			rabbitTemplate.convertAndSend(RabbitConstant.Exchange.INTELLIGENCE_INVEST_CANCEL_AMOUNT, RabbitConstant.RoutingKey.INTELLIGENCE_INVEST_CANCEL_AMOUNT, packageInvestOrderId);
		}

	}

	/**
	 * 投资金额 = 剩余可投 不验证 以下操作 验证 资金是否大于最小投资金额 验证 资金是否按照规则进行递增
	 *
	 * @param investAmount
	 * @param projectPackage
	 */
	private void validateIntelligenceInvestAmount(BigDecimal investAmount, ProjectPackage projectPackage) {
		// 最低投资金额
		BigDecimal minInvestAmonut = projectPackage.getMinInvestAmount();
		// 投资递增金额
		BigDecimal investIncrementAmount = projectPackage.getInvestIncrementAmount();
		// 可投资金额
		BigDecimal avalibleInvestAmunt = projectPackage.getAmount().subtract(projectPackage.getInvestedAmount());
		int result = 0;
		// 投资金额 == 剩余可投金额 不做最小投资金额和递增规则验证
		if ((result = avalibleInvestAmunt.compareTo(investAmount)) == 0) {
			return;
		}
		if (result < 0) {
			// 智能投可投资金小于投资金额
			throw new BizException("TRD10038");
		}
		if (result > 0) {
			// 投资金额小于最小起投金额
			if (minInvestAmonut.compareTo(investAmount) > 0) {
				if (avalibleInvestAmunt.compareTo(minInvestAmonut) > 0) {
					throw new BizException("TRD10039");
				}
				// 低于最低投资金额的时候，100起投100递增
				minInvestAmonut = BigDecimal.valueOf(100);
				investIncrementAmount = BigDecimal.valueOf(100);
			}
			// 需要 (投资金额-最小投资金额)%递增金额 = 0
			BigDecimal[] WholeHundred = investAmount.subtract(minInvestAmonut).divideAndRemainder(investIncrementAmount, MathContext.DECIMAL32);
			if (BigDecimal.ZERO.compareTo(WholeHundred[1]) != 0) {
				// 当前投资规则为100元起投100元递增
				if (avalibleInvestAmunt.compareTo(minInvestAmonut) < 0) {
					throw new BizException("TRD10044");
				}
				// 投资金额必须符合智能投金额递增规则
				throw new BizException("TRD10040");
			}
		}

	}

	/**
	 * 验证智能投红包是否可以使用
	 * 
	 * @param request
	 */
	private UserPrizeResponseDTO getAndvalidateIntelligenceInvestPrize(IntelligenceInvestDubboRequest request) {
		Long prizeId = request.getPrizeId();
		String userId = request.getUserId();
		UserPrizeResponseDTO userPrizeResponseDTO = null;
		if (null != prizeId && 0 != prizeId) {
			try {
				userPrizeResponseDTO = activityPrizeDubboService.getUserPrizeInfo(userId, prizeId);
			} catch (Throwable e) {
				if (e instanceof BizException) {
					throw e;
				}
				throw new BizException("TRD30003");
			}
			if (null == userPrizeResponseDTO) {
				throw new BizException("TRD30007");// 红包不存在
			}
			// 8投资加息券，9投资返现红包
			if (!Byte.valueOf("8").equals(userPrizeResponseDTO.getPrizeTypeId()) && !Byte.valueOf("9").equals(userPrizeResponseDTO.getPrizeTypeId())) {
				throw new BizException("TRD30008");// 红包类型错误
			}
			if (Byte.valueOf("8").equals(userPrizeResponseDTO.getPrizeTypeId())) {
				if (null == userPrizeResponseDTO.getInvestRate() || userPrizeResponseDTO.getInvestRate().compareTo(BigDecimal.ZERO) <= 0 || userPrizeResponseDTO.getNumberMount() == null) {
					throw new BizException("TRD30009");// 该投资加息券数据有误
				}
			}

		}
		return userPrizeResponseDTO;
	}

	/**
	 * 生成包投资订单 生成投资订单 修改标，投资中的标数据及状态
	 * 
	 * @param projectPackageInvestOrder
	 * @param projectPackage
	 * @param investing
	 * @param request
	 * @param userPrizeResponseDTO
	 * @param accountBase
	 * @return
	 */
	private List<ProjectInvestOrder> generateIntelligenceInvestOrder(ProjectPackageInvestOrder projectPackageInvestOrder, ProjectPackage projectPackage, List<ProjectInvesting> investing,
			IntelligenceInvestDubboRequest request, UserPrizeResponseDTO userPrizeResponseDTO, AccountBaseResponse accountBase) {
		// 排除你的个人借款，可投资金额不足
		if (CollectionUtils.isEmpty(investing)) {
			throw new BizException("TRD10043");
		}
		// 锁住防止定时任务触发
		projectPackageInvestOrderMapperExt.selectByPrimaryKeyForUpdate(projectPackageInvestOrder.getId());
		BigDecimal one = BigDecimal.valueOf(100L);// 最少一份
		BigDecimal allInterest = BigDecimal.ZERO;// 所有收益 利息+活动加息
		BigDecimal allAddInterest = BigDecimal.ZERO;// 加息券收益
		BigDecimal allAmount = request.getInvestAmount();// 总投资金额
		BigDecimal nextAdd = BigDecimal.ZERO;// 金额下移
		Long prizeId = request.getPrizeId();// 红包ID
		BigDecimal addRate = BigDecimal.ZERO;// 加息券加息率
		if (Objects.nonNull(userPrizeResponseDTO) && Byte.valueOf("8").equals(userPrizeResponseDTO.getPrizeTypeId())) {
			addRate = userPrizeResponseDTO.getInvestRate().divide(new BigDecimal(100));
		}
		// 每一笔投资金额 = (投资金额/100/剩余可投标的数)(向上取整后)*100 100为了方便取整
		BigDecimal everyShareAmount = allAmount.divide(one).divide(BigDecimal.valueOf(investing.size()), 0, RoundingMode.UP).multiply(one);
		String userId = request.getUserId();
		List<ProjectInvestOrder> investOrders = new ArrayList<>(investing.size());// 生成的投资订单
		BigDecimal allowInvestAmount = BigDecimal.ZERO;// 每一笔需要投资的金额
		BigDecimal investAmount = BigDecimal.ZERO;// 投资金额
		BigDecimal activityRaiseRate = projectPackage.getActivityRaiseRate();
		List<PackageInvestPrizeOccupiedReqDTO.InvestOrderDetailDTO> activityOrderInfo = new ArrayList<>(investing.size());
		String name = "";// 投资人名称
		String idCard = "";// 身份标志
		// 企业与用户保存的信息不一致修改
		if (UserTypeEnum.ORGANIZATION.getValue().equals(request.getInvestUserType())) {
			SimpleOrgInfo orgInfo = organizationDubboService.getByOrgId(userId);
			name = orgInfo.getOrganizationName();
			try {
				idCard = privacyMasks.encryptPrivacy(orgInfo.getUnifiedSocialCreditCode());
			} catch (Exception ex) {
				log.error("投资订单社会统一编码加密出错{}", ex);
			}
		} else {
			UserIdentityInfoResponseDTO identityResponseDTO = userIdentityDubboService.get(userId);
			name = identityResponseDTO.getName();
			try {
				idCard = privacyMasks.encryptPrivacy(identityResponseDTO.getIdCard());
			} catch (Exception ex) {
				log.error("投资订单身份证号加密出错{}", ex);
			}
		}

		for (ProjectInvesting projectInvesting : investing) {
			// 可投资金额不足退出投资
			if (allAmount.compareTo(BigDecimal.ZERO) == 0) {
				break;
			}
			Long investOrderId = snowflakeIdWorker.nextId();
			Long projectId = projectInvesting.getProjectId();

			allowInvestAmount = projectInvesting.getContractAmount().subtract(projectInvesting.getInvestedAmount());
			investAmount = everyShareAmount.add(nextAdd);
			// 分资金后的投资金额大于可投资金额的时候，需要扣减留给下一个标
			if (investAmount.compareTo(allowInvestAmount) <= 0) {
				nextAdd = BigDecimal.ZERO;
			} else {
				nextAdd = investAmount.subtract(allowInvestAmount);
				investAmount = allowInvestAmount;
			}
			investAmount = allAmount.min(investAmount);
			allAmount = allAmount.subtract(investAmount);
			// 异步的存管投资有可能造成已投金额回退，锁表
			Project project = projectMapperExt.selectByPrimaryKeyForUpdate(projectId);
			if (!project.getStatus().equals(ProjectStatusEnum.COLLECTING.getStatus())) {
				throw new BizException("TRD10036");
			}
			// 增加投资订单
			ProjectInvestOrder projectInvestOrder = new ProjectInvestOrder();

			// 生成红包订单
			if (Objects.nonNull(prizeId) && prizeId > 0) {
				ProjectInvestPrize projectInvestPrize = new ProjectInvestPrize();
				projectInvestPrize.setId(investOrderId);
				projectInvestPrize.setUserId(userId);
				projectInvestPrize.setProjectId(projectId);
				projectInvestPrize.setInvestOrderId(investOrderId);
				projectInvestPrize.setPrizeId(prizeId);
				projectInvestPrize.setType(Byte.valueOf("1"));
				projectInvestPrize.setCreateTime(new Date());
				projectInvestPrize.setInvestType(OrderInvestTypeEnum.INTELLIGENCE.getType());
				projectInvestPrizeMapperExt.insertSelective(projectInvestPrize);
				// 投资订单红包相关
				projectInvestOrder.setPrizeId(prizeId);
				projectInvestOrder.setPrizeTypteId(userPrizeResponseDTO.getPrizeTypeId());
				projectInvestOrder.setPrizeName(userPrizeResponseDTO.getPrizeName());
				if (Byte.valueOf("8").equals(userPrizeResponseDTO.getPrizeTypeId())) {
					projectInvestOrder.setAddRate(addRate);
					BigDecimal addInterest = investCommonServer.getAddInterest(project, investAmount, userPrizeResponseDTO);
					projectInvestOrder.setAddInterest(addInterest);
					allAddInterest = allAddInterest.add(addInterest);
				}
				// 红包占用
				PackageInvestPrizeOccupiedReqDTO.InvestOrderDetailDTO activityOrtder = new PackageInvestPrizeOccupiedReqDTO.InvestOrderDetailDTO();
				activityOrtder.setAmount(investAmount);
				activityOrtder.setProjectId(projectId);
				activityOrtder.setInvestOrderId(investOrderId);
				activityOrtder.setInvestTime(new Date());
				activityOrderInfo.add(activityOrtder);
			}
			// 修改标的可投资金额
			Project newProjectInfo = new Project();
			newProjectInfo.setProjectId(projectId);
			newProjectInfo.setInvestedAmount(project.getInvestedAmount().add(investAmount));
			projectMapperExt.updateByPrimaryKeySelective(newProjectInfo);
			// 修改投资中订单
			ProjectInvesting newProjectInvesting = new ProjectInvesting();
			newProjectInvesting.setProjectId(projectId);
			BigDecimal newInvestAmount = projectInvesting.getInvestedAmount().add(investAmount);
			newProjectInvesting.setInvestedAmount(newInvestAmount);
			// 是否压标
			projectInvestOrder.setIsFull(Byte.valueOf("0"));
			if (projectInvesting.getContractAmount().compareTo(newInvestAmount) == 0) {
				newProjectInvesting.setStatus(Byte.valueOf("2"));// 已抢光
				projectInvestOrder.setIsFull(Byte.valueOf("1"));
			}
			projectInvestingMapperExt.updateByPrimaryKeySelective(newProjectInvesting);
			// 插入投资订单
			projectInvestOrder.setId(investOrderId);
			projectInvestOrder.setProjectId(projectId);
			projectInvestOrder.setProjectType(projectInvesting.getType());
			projectInvestOrder.setBorrowUserId(projectInvesting.getBorrowUserId());
			projectInvestOrder.setBorrowHeadImage(projectInvesting.getBorrowHeadImage());
			projectInvestOrder.setBorrowNickName(projectInvesting.getBorrowNickName());
			projectInvestOrder.setBorrowGender(projectInvesting.getBorrowGender());
			projectInvestOrder.setBorrowNiiwooScore(projectInvesting.getBorrowNiiwooScore());
			projectInvestOrder.setLoanTimes(projectInvesting.getLoanTimes());
			projectInvestOrder.setBorrowRate(projectInvesting.getBorrowRate());
			projectInvestOrder.setTitle(projectInvesting.getTitle());
			projectInvestOrder.setContractAmount(projectInvesting.getContractAmount());
			projectInvestOrder.setDeadline(projectInvesting.getDeadline());
			projectInvestOrder.setDeadlineUnit(projectInvesting.getDeadlineUnit());
			projectInvestOrder.setLatitude(projectInvesting.getLatitude());
			projectInvestOrder.setLongtitude(projectInvesting.getLongitude());
			projectInvestOrder.setLoanCity(projectInvesting.getLoanCity());
			projectInvestOrder.setLoanArea(projectInvesting.getLoanArea());
			projectInvestOrder.setPublishTime(projectInvesting.getPublishTime());
			projectInvestOrder.setRepaymentType(projectInvesting.getRepaymentType());
			projectInvestOrder.setUserId(userId);
			projectInvestOrder.setInvestorName(name);
			projectInvestOrder.setInvestorIdCard(idCard);
			projectInvestOrder.setAccountId(accountBase.getAccountId());
			// 增加投资人平台用户编码
			projectInvestOrder.setPlatformUserNo(accountBase.getAccountNo());
			projectInvestOrder.setInvestType(OrderInvestTypeEnum.INTELLIGENCE.getType());// 智能投
			projectInvestOrder.setAmount(investAmount);
			List<RepaymentPlanDTO> repaymentPlan = RepaymentPlanContext.getRepaymentPlan(RepaymentTypeEnum.enumOf(project.getRepaymentType()), project.getType(), investAmount,
					projectPackage.getBorrowRate(), project.getDeadline(), PeriodUnitEnum.enumOf(project.getDeadlineUnit()), new Date());
			BigDecimal interest = BigDecimal.ZERO;
			for (RepaymentPlanDTO repaymentPlanDTO : repaymentPlan) {
				interest = interest.add(repaymentPlanDTO.getInterest());
			}
			log.info("investOrderId:{},interest:{}", investOrderId, interest);

			if (Objects.nonNull(activityRaiseRate) && BigDecimal.ZERO.compareTo(activityRaiseRate) < 0) {
				List<RepaymentPlanDTO> activityRaiseRepaymentPlan = RepaymentPlanContext.getRepaymentPlan(RepaymentTypeEnum.enumOf(project.getRepaymentType()), project.getType(), investAmount,
						activityRaiseRate, project.getDeadline(), PeriodUnitEnum.enumOf(project.getDeadlineUnit()), new Date());
				for (RepaymentPlanDTO repaymentPlanDTO : activityRaiseRepaymentPlan) {
					interest = interest.add(repaymentPlanDTO.getInterest());
				}
				log.info("investOrderId:{},activityRaiseRate after interest:{}", investOrderId, interest);
			}
			projectInvestOrder.setInterest(interest);
			projectInvestOrder.setStatus(ProjectInvestOrderStatusEnum.PENDING_PAYMENT.getValue());
			projectInvestOrder.setEvolutionStatus(ProjectInvestOrderEvolutionStatusEnum.DEALING.getValue());
			projectInvestOrder.setType(InvestWayEnum.MANUAL_DIRECT.getValue());// 默认免密
			projectInvestOrder.setExpireTime(new DateTime(new Date()).plusMinutes(10).toDate());
			projectInvestOrder.setCreateTime(new Date());
			// 已作废字段，不使用
			projectInvestOrder.setOverdueType(OverdueTypeEnum._31D_RULE.getCode());
			projectInvestOrder.setInvestorType(request.getInvestUserType());// 投资人类型

			projectInvestOrderMapperExt.insertSelective(projectInvestOrder);
			ProjectPackageInvestOrderDetail detail = new ProjectPackageInvestOrderDetail();
			detail.setCreateTime(new Date());
			detail.setId(snowflakeIdWorker.nextId());
			detail.setInvestOrderId(investOrderId);
			detail.setPackageInvestOrderId(projectPackageInvestOrder.getId());
			detail.setUpdateTime(new Date());
			projectPackageInvestOrderDetailMapperExt.insertSelective(detail);
			// 总收益
			allInterest = allInterest.add(interest);
			investOrders.add(projectInvestOrder);
		}
		// 排除你的个人借款，可投资金额不足
		if (allAmount.compareTo(BigDecimal.ZERO) > 0) {
			throw new BizException("TRD10043");
		}
		// 包投资订单生成
		projectPackageInvestOrder.setAddInterest(allAddInterest);
		projectPackageInvestOrder.setAddRate(addRate);
		projectPackageInvestOrder.setInterest(allInterest);
		if (Objects.nonNull(prizeId) && prizeId > 0) {
			projectPackageInvestOrder.setPrizeId(prizeId);
			projectPackageInvestOrder.setPrizeName(userPrizeResponseDTO.getPrizeName());
			projectPackageInvestOrder.setPrizeTypteId(userPrizeResponseDTO.getPrizeTypeId());
		}
		projectPackageInvestOrder.setProjectNum(investOrders.size());
		projectPackageInvestOrder.setRealInvestAmount(request.getInvestAmount());
		projectPackageInvestOrder.setUpdateTime(new Date());
		projectPackageInvestOrderMapperExt.updateByPrimaryKey(projectPackageInvestOrder);

		if (Objects.nonNull(prizeId) && prizeId > 0) {
			// 红包占用
			PackageInvestPrizeOccupiedReqDTO prizeOccupiedReqDTO = new PackageInvestPrizeOccupiedReqDTO();
			prizeOccupiedReqDTO.setOrderNo(projectPackageInvestOrder.getId());
			prizeOccupiedReqDTO.setPackageId(projectPackage.getPackageId());
			prizeOccupiedReqDTO.setPackageInvestOrderId(projectPackageInvestOrder.getId());
			prizeOccupiedReqDTO.setPrizeId(prizeId);
			prizeOccupiedReqDTO.setTotalInvestAmount(request.getInvestAmount());
			prizeOccupiedReqDTO.setUserId(userId);
			prizeOccupiedReqDTO.setInvestOrderDetailDTOList(activityOrderInfo);
			prizeOccupiedReqDTO.setDeadLine(projectPackage.getDeadline());
			prizeOccupiedReqDTO.setSuitProjectTypeEnum(UserPrizeSuitProjectTypeEnum.PACKAGE_INVEST);
			try {
				// 红包占用
				activityPrizeDubboService.packageInvestPrizeOccupied(prizeOccupiedReqDTO);
			} catch (Exception e) {
				// 红包占用使用，发起解除占用，
				boolean prizeBackFlag = true;
				ActivityPrizeResetStatusReqDTO activityPrizeResetStatusReqDTO = new ActivityPrizeResetStatusReqDTO();
				activityPrizeResetStatusReqDTO.setPrizeId(prizeId);
				activityPrizeResetStatusReqDTO.setUserId(userId);
				activityPrizeResetStatusReqDTO.setInvestOrderId(projectPackageInvestOrder.getId());
				try {
					activityPrizeDubboService.resetPrizeToReturned(activityPrizeResetStatusReqDTO);
				} catch (BizException e1) {
					if (!"ACTSTAGE10025".equals(e1.getErrorCode())) {
						prizeBackFlag = false;
					}
				}
				// 红包解除失败，发起红包补单回退
				if (!prizeBackFlag) {
					// 生成红包退回信息
					intelligenceInvestFlowService.generatePrizeRedBack(prizeOccupiedReqDTO);
				}
				throw new BizException("TRD10037");// 红包使用失败
			}
		}

		return investOrders;
	}

	/**
	 * 智能投包流标事件
	 */
	public void flow() {
		List<ProjectPackage> endTimeProjectPackage = projectPackageMapperExt.selectEndTimeProjectPackage();
		if (CollectionUtils.isNotEmpty(endTimeProjectPackage)) {
			log.info("智能投流包处理开始，需要流包数量{}", endTimeProjectPackage.size());
			Long time = System.currentTimeMillis();
			for (ProjectPackage projectPackage : endTimeProjectPackage) {
				intelligenceInvestFlowService.flowPackage(projectPackage.getPackageId());
			}
			log.info("智能投流包处理结束，总笔数{}，总耗时{}", Objects.nonNull(endTimeProjectPackage) ? endTimeProjectPackage.size() : 0, System.currentTimeMillis() - time);
		}
	}

	/**
	 * 流包处理
	 *
	 * @param projectPackageInvestOrderId
	 * @param investOrderId
	 * @param closeReason
	 */
	@Transactional(isolation = READ_COMMITTED)
	public void investCancelSuccess(Long projectPackageInvestOrderId, Long investOrderId, InvestOrderCloseReasonEnum closeReason) {
		ProjectPackageInvestOrder projectPackageInvestOrder = projectPackageInvestOrderMapperExt.selectByPrimaryKey(projectPackageInvestOrderId);
		ProjectInvestOrder projectInvestOrder = projectInvestOrderMapperExt.selectByPrimaryKey(investOrderId);
		List<ProjectPackageInvestOrderDetail> details = projectPackageInvestOrderDetailMapperExt.selectByPackageInvestOrderId(projectPackageInvestOrderId);
		Set<Long> investOrderIds = new HashSet<>();
		for (ProjectPackageInvestOrderDetail detail : details) {
			investOrderIds.add(detail.getInvestOrderId());
		}
		// 投资成功的才会触发流包操作
		if (PackageProjectInvestOrderStatusEnum.SUCCESS.getValue().equals(projectPackageInvestOrder.getStatus())) {
			// 先锁住，防止锁冲突
			ProjectPackageInvestOrder packageInvestOrderUpdate = projectPackageInvestOrderMapperExt.selectByPrimaryKeyForUpdate(projectPackageInvestOrderId);
			// 投资取消 定时任务/投资订单
			investCommonServer.investCancelSuccess(investOrderId, closeReason);
			List<ProjectInvestOrder> orders = projectInvestOrderMapperExt.selectByInvestOrderIds(investOrderIds);
			int closedNum = 0;// 已关闭
			int investing = 0;// 投资中
			for (ProjectInvestOrder order : orders) {
				if (ProjectInvestOrderEvolutionStatusEnum.BACKING.getValue().equals(order.getEvolutionStatus())) {
					continue;
				} else if (ProjectInvestOrderEvolutionStatusEnum.CLOSED.getValue().equals(order.getEvolutionStatus())) {
					closedNum++;
				} else {
					investing++;
				}
			}
			ProjectPackageInvestOrder newPackageInvestOrderUpdate = new ProjectPackageInvestOrder();

			newPackageInvestOrderUpdate.setUpdateTime(new Date());
			newPackageInvestOrderUpdate.setId(projectPackageInvestOrderId);
			newPackageInvestOrderUpdate.setAddInterest(packageInvestOrderUpdate.getAddInterest().subtract(projectInvestOrder.getAddInterest()));
			newPackageInvestOrderUpdate.setRealInvestAmount(packageInvestOrderUpdate.getRealInvestAmount().subtract(projectInvestOrder.getAmount()));
			newPackageInvestOrderUpdate.setInterest(packageInvestOrderUpdate.getInterest().subtract(projectInvestOrder.getInterest()));
			// 没有投资中的需要修改状态
			if (investing == 0) {
				// 全部取消 订单关闭
				if (closedNum == projectPackageInvestOrder.getProjectNum().intValue()) {
					newPackageInvestOrderUpdate.setStatus(PackageProjectInvestOrderStatusEnum.CANCEL.getValue());
					newPackageInvestOrderUpdate.setEvolutionStatus(PackageInvestOrderEvolutionStatusEnum.CLOSED.getValue());
					// 筹款超时
					newPackageInvestOrderUpdate.setCloseReason(Byte.valueOf("1"));
					newPackageInvestOrderUpdate.setCloseTime(new Date());
					// 只要存在成功的，订单为回款中
				} else {
					newPackageInvestOrderUpdate.setStatus(PackageProjectInvestOrderStatusEnum.SUCCESS.getValue());
					newPackageInvestOrderUpdate.setEvolutionStatus(PackageInvestOrderEvolutionStatusEnum.BACKING.getValue());
				}
				// 调用智能投订单
				TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
					@Override
					public void afterCompletion(int status) {
						if (status == TransactionSynchronizationAdapter.STATUS_COMMITTED) {
							rabbitTemplate.convertAndSend(RabbitConstant.Exchange.INTELLIGENCE_INVEST_END, RabbitConstant.RoutingKey.INTELLIGENCE_INVEST_END, projectPackageInvestOrder.getId());
						}
					}
				});

			}
			projectPackageInvestOrderMapperExt.updateByPrimaryKeySelective(newPackageInvestOrderUpdate);

		}
	}

	/**
	 * 防止丢失调用，十分钟还在投资中的订单再次触发
	 */
	public void triggerInvest() {
		List<ProjectPackageInvestOrder> result = projectPackageInvestOrderMapperExt.selectOldInvestingOrderByType(PackageProjectInvestOrderStatusEnum.PENDING_PAYMENT.getValue());
		if (CollectionUtils.isNotEmpty(result)) {
			log.info("智能投再出触发投资任务，需要触发任务个数{}", result.size());
			for (ProjectPackageInvestOrder projectPackageInvestOrder : result) {
				RpcContext.getContext().asyncCall(() -> accountInvestDubboService.intelligenceInvest(projectPackageInvestOrder.getId()));
				// 智能投合同MQ 涉及 风险提示书 智能投合同 智能投风险书
				rabbitTemplate.convertAndSend(RabbitConstant.Exchange.INTELLIGENCE_INVEST_CONTRACT, RabbitConstant.RoutingKey.INTELLIGENCE_INVEST_CONTRACT, projectPackageInvestOrder.getId());
			}
		}

	}

	/**
	 * 防止丢失调用，十分钟还在关闭中的的订单再次触发
	 */
	public void triggerCancel() {
		List<ProjectPackageInvestOrder> result = projectPackageInvestOrderMapperExt.selectOldInvestingOrderByType(PackageProjectInvestOrderStatusEnum.CLOSING.getValue());
		if (CollectionUtils.isNotEmpty(result)) {
			log.info("智能投再出触发取消投资任务，需要触发任务个数{}", result.size());
			for (ProjectPackageInvestOrder projectPackageInvestOrder : result) {
				RpcContext.getContext().asyncCall(() -> accountInvestDubboService.intelligenceCancel(projectPackageInvestOrder.getId()));
			}
		}

	}

	/**
	 * 获取订单的存管调用信息 同步问题查询不到，使用主库
	 * 
	 * @param projectPackageOrderId
	 * @return
	 */
	@Transactional
	public List<IntelligencePayInfoResponseDTO> getIntelligencePayInfo(Long projectPackageOrderId) {
		List<ProjectPackageInvestOrderDetail> result = projectPackageInvestOrderDetailMapperExt.selectByPackageInvestOrderId(projectPackageOrderId);
		Set<Long> investOrderIds = new HashSet<>();
		for (ProjectPackageInvestOrderDetail projectPackageInvestOrderDetail : result) {
			investOrderIds.add(projectPackageInvestOrderDetail.getInvestOrderId());
		}
		List<ProjectInvestOrder> orders = projectInvestOrderMapperExt.selectByInvestOrderIds(investOrderIds);
		List<IntelligencePayInfoResponseDTO> payInfos = new ArrayList<>();
		for (ProjectInvestOrder order : orders) {
			IntelligencePayInfoResponseDTO payInfo = new IntelligencePayInfoResponseDTO();
			payInfo.setBorrowerUserId(order.getBorrowUserId());
			payInfo.setDeadline(order.getDeadline());
			payInfo.setDeadlineUnit(order.getDeadlineUnit());
			payInfo.setExpired(order.getExpireTime());
			payInfo.setInvestAmount(order.getAmount());
			payInfo.setInvestOrderId(order.getId());
			payInfo.setInvestType(order.getInvestType());
			payInfo.setProjectId(order.getProjectId());
			payInfo.setProjectPackageInvestOrderId(projectPackageOrderId);
			payInfo.setProjectTitle(order.getTitle());
			payInfo.setProjectType(order.getProjectType());
			payInfo.setUserId(order.getUserId());
			payInfos.add(payInfo);
		}

		return payInfos;
	}

	/**
	 * 智能投订单冻结成功回调
	 * 
	 * @param packageInvestOrderId
	 */
	@Transactional(isolation = READ_COMMITTED)
	public void intelligenceInvestSuccess(Long packageInvestOrderId) {

		ProjectPackageInvestOrder projectPackageInvestOrder = projectPackageInvestOrderMapperExt.selectByPrimaryKeyForUpdate(packageInvestOrderId);
		// 如果不是待支付中的状态不处理
		if (!PackageProjectInvestOrderStatusEnum.PENDING_PAYMENT.getValue().equals(projectPackageInvestOrder.getStatus())) {
			return;
		}
		projectPackageInvestOrder.setStatus(PackageProjectInvestOrderStatusEnum.SUCCESS.getValue());
		projectPackageInvestOrder.setEvolutionStatus(PackageInvestOrderEvolutionStatusEnum.INVESTING.getValue());
		projectPackageInvestOrder.setUpdateTime(new Date());
		projectPackageInvestOrderMapperExt.updateByPrimaryKeySelective(projectPackageInvestOrder);

		Long prizeId = projectPackageInvestOrder.getPrizeId();

		List<ProjectPackageInvestOrderDetail> orders = projectPackageInvestOrderDetailMapperExt.selectByPackageInvestOrderId(packageInvestOrderId);
		for (ProjectPackageInvestOrderDetail projectPackageInvestOrderDetail : orders) {
			// 投资订单状态修改
			ProjectInvestOrder projectInvestOrder = new ProjectInvestOrder();
			projectInvestOrder.setId(projectPackageInvestOrderDetail.getInvestOrderId());
			projectInvestOrder.setUpdateTime(new Date());
			projectInvestOrder.setStatus(Byte.valueOf("1"));
			projectInvestOrder.setEvolutionStatus(ProjectInvestOrderEvolutionStatusEnum.INVESTING.getValue());
			projectInvestOrderMapperExt.updateByPrimaryKeySelective(projectInvestOrder);
			// 红包占用
			if (Objects.nonNull(prizeId)) {
				ProjectInvestPrize projectInvestPrize = new ProjectInvestPrize();
				projectInvestPrize.setId(projectPackageInvestOrderDetail.getInvestOrderId());
				projectInvestPrize.setStatus(Byte.valueOf("1"));
				projectInvestPrize.setUpdateTime(new Date());
				projectInvestPrizeMapperExt.updateByPrimaryKeySelective(projectInvestPrize);
			}
			projectInvestOrder = projectInvestOrderMapperExt.selectByPrimaryKey(projectPackageInvestOrderDetail.getInvestOrderId());

			Project project = projectMapperExt.selectByPrimaryKey(projectInvestOrder.getProjectId());
			if(enableAddGroup(projectInvestOrder.getProjectType(), projectInvestOrder.getInvestorType(), project.getOrgId())) {
				// 插入建项目群任务
				ProjectInvestOrderProcess projectInvestOrderProcess = new ProjectInvestOrderProcess();
				projectInvestOrderProcess.setInvestOrderId(projectPackageInvestOrderDetail.getInvestOrderId());
				projectInvestOrderProcess.setProjectId(projectInvestOrder.getProjectId());
				projectInvestOrderProcess.setProjectType(projectInvestOrder.getProjectType());
				projectInvestOrderProcess.setBorrowUserId(projectInvestOrder.getBorrowUserId());
				projectInvestOrderProcess.setTitle(projectInvestOrder.getTitle());
				projectInvestOrderProcess.setUserId(projectInvestOrder.getUserId());
				projectInvestOrderProcess.setAddGroupStatus(Byte.valueOf("1"));
				projectInvestOrderProcess.setCreateTime(new Date());
				projectInvestOrderProcess.setUpdateTime(new Date());
				projectInvestOrderProcessMapperExt.insertSelective(projectInvestOrderProcess);
			}

			// 投资成功后置处理
			rabbitTemplate.convertAndSend(RabbitConstant.Exchange.INVEST_POST_PROCESS, RabbitConstant.RoutingKey.INVEST_POST_PROCESS, projectPackageInvestOrderDetail.getInvestOrderId());
		}

	}

	/**
	 * 智能投订单撤销, 第一步先修改订单状态，方便后续异常能够定时在才触发
	 * 
	 * @param projectPackageOrderId
	 */
	@Transactional
	public void intelligenceInvestPreCancel(Long projectPackageOrderId) {
		ProjectPackageInvestOrder projectPackageInvestOrder = new ProjectPackageInvestOrder();
		projectPackageInvestOrder.setId(projectPackageOrderId);
		projectPackageInvestOrder.setStatus(PackageProjectInvestOrderStatusEnum.CLOSING.getValue());
		projectPackageInvestOrder.setUpdateTime(new Date());
		projectPackageInvestOrderMapperExt.updateByPrimaryKeySelective(projectPackageInvestOrder);
	}

	/**
	 * 智能投订单撤销 第二步骤，
	 * 
	 * @param projectPackageOrderId
	 */
	@Transactional(isolation = READ_COMMITTED)
	public void intelligenceCancel(Long projectPackageOrderId) {
		ProjectPackageInvestOrder projectPackageInvestOrder = projectPackageInvestOrderMapperExt.selectByPrimaryKey(projectPackageOrderId);
		List<ProjectPackageInvestOrderDetail> details = projectPackageInvestOrderDetailMapperExt.selectByPackageInvestOrderId(projectPackageOrderId);
		Set<Long> investOrderIds = new HashSet<>();
		for (ProjectPackageInvestOrderDetail detail : details) {
			investOrderIds.add(detail.getInvestOrderId());
		}
		// 非关闭中不处理 关闭中，标的已投金额，投资中的标已投金额，包的已投金额做扣减,投资包状态修改
		if (!PackageProjectInvestOrderStatusEnum.CLOSING.getValue().equals(projectPackageInvestOrder.getStatus())) {
			return;
		}
		// 防止死锁，大范围的表先锁，后锁小范围的
		// 智能投包修改已投金额
		ProjectPackage projectPackage = projectPackageMapperExt.selectByPrimaryKeyForUpdate(projectPackageInvestOrder.getPackageId());
		// 先锁住，防止锁冲突
		ProjectPackageInvestOrder packageInvestOrderUpdate = projectPackageInvestOrderMapperExt.selectByPrimaryKeyForUpdate(projectPackageOrderId);
		// 非关闭中不处理 关闭中，标的已投金额，投资中的标已投金额，包的已投金额做扣减,投资包状态修改
		if (!PackageProjectInvestOrderStatusEnum.CLOSING.getValue().equals(projectPackageInvestOrder.getStatus())) {
			return;
		}
		ProjectPackage newProjectPackage = new ProjectPackage();
		newProjectPackage.setInvestedAmount(projectPackage.getInvestedAmount().subtract(projectPackageInvestOrder.getAmount()));
		newProjectPackage.setPackageId(projectPackageInvestOrder.getPackageId());
		newProjectPackage.setUpdateTime(new Date());
		// 由于扣减金额，状态改为投资中
		newProjectPackage.setStatus(ProjectPackageStatusEnum.INVESTING.getStatus());
		projectPackageMapperExt.updateByPrimaryKeySelective(newProjectPackage);

		List<ProjectInvestOrder> investOrders = projectInvestOrderMapperExt.selectByInvestOrderIds(investOrderIds);
		for (ProjectInvestOrder projectInvestOrder : investOrders) {
			// 投资中的标修改已投资金额
			Project project = projectMapperExt.selectByPrimaryKeyForUpdate(projectInvestOrder.getProjectId());
			Project newProject = new Project();
			newProject.setProjectId(projectInvestOrder.getProjectId());
			newProject.setInvestedAmount(project.getInvestedAmount().subtract(projectInvestOrder.getAmount()));
			newProject.setUpdateTime(new Date());
			projectMapperExt.updateByPrimaryKeySelective(newProject);
			// 投资中的标的
			ProjectInvesting projectInvesting = new ProjectInvesting();
			projectInvesting.setProjectId(projectInvestOrder.getProjectId());
			projectInvesting.setInvestedAmount(newProject.getInvestedAmount());
			projectInvesting.setUpdateTime(new Date());
			projectInvesting.setStatus(Byte.valueOf("1"));
			projectInvestingMapperExt.updateByPrimaryKeySelective(projectInvesting);
			// 投资订单
			ProjectInvestOrder newProjectInvestOrder = new ProjectInvestOrder();
			newProjectInvestOrder.setId(projectInvestOrder.getId());
			newProjectInvestOrder.setEvolutionStatus(ProjectInvestOrderEvolutionStatusEnum.CLOSED.getValue());
			newProjectInvestOrder.setClosedReason(InvestOrderCloseReasonEnum.PAY_CANCELED.getValue());
			newProjectInvestOrder.setUpdateTime(new Date());
			newProjectInvestOrder.setStatus(ProjectInvestOrderStatusEnum.CANCEL.getValue());
			projectInvestOrderMapperExt.updateByPrimaryKeySelective(newProjectInvestOrder);
		}

		ProjectPackageInvestOrder newPackageInvestOrderUpdate = new ProjectPackageInvestOrder();
		newPackageInvestOrderUpdate.setCloseTime(new Date());
		newPackageInvestOrderUpdate.setStatus(PackageProjectInvestOrderStatusEnum.CANCEL.getValue());
		newPackageInvestOrderUpdate.setEvolutionStatus(PackageInvestOrderEvolutionStatusEnum.CLOSED.getValue());
		// 订单调用存管失败
		newPackageInvestOrderUpdate.setCloseReason(Byte.valueOf("2"));
		newPackageInvestOrderUpdate.setUpdateTime(new Date());
		newPackageInvestOrderUpdate.setId(packageInvestOrderUpdate.getId());
		projectPackageInvestOrderMapperExt.updateByPrimaryKeySelective(newPackageInvestOrderUpdate);
		// 发送通知消息
		rabbitTemplate.convertAndSend(RabbitConstant.Exchange.INTELLIGENCE_INVEST_END, RabbitConstant.RoutingKey.INTELLIGENCE_INVEST_END, projectPackageInvestOrder.getId());
	}

	/**
	 * 智能投签署合同
	 * 
	 * @param projectPackageInvestOrderId
	 * @throws Exception
	 */
	public void initIntelligenceContract(Long projectPackageInvestOrderId) throws Exception {

		ProjectPackageInvestOrder packageInvestOrder = projectPackageInvestOrderMapperExt.selectByPrimaryKey(projectPackageInvestOrderId);
		if (Objects.isNull(packageInvestOrder)) {
			return;
		}
		List<ProjectPackageInvestOrderDetail> details = projectPackageInvestOrderDetailMapperExt.selectByPackageInvestOrderId(projectPackageInvestOrderId);
		Set<Long> orderIds = new HashSet<>();
		for (ProjectPackageInvestOrderDetail detail : details) {
			orderIds.add(detail.getInvestOrderId());
		}
		// 风险提示书
		List<ProjectInvestOrder> orders = projectInvestOrderMapperExt.selectByInvestOrderIds(orderIds);
		for (ProjectInvestOrder projectInvestOrder : orders) {
			ProjectContract pc = projectContractMapperExt.queryProjectContractByUserIdAndProjectIdAndContractType(projectInvestOrder.getUserId(), projectInvestOrder.getProjectId(),
					ContractTypeEnum.RISK_PROMPT.getType());
			if (Objects.isNull(pc) || Objects.isNull(pc.getViewUrl()) || Objects.isNull(pc.getDownloadUrl())) {
				// 风险提示书签署法大大合同
				rabbitTemplate.convertAndSend(RabbitConstant.Exchange.SIGN_RISK_TIPS_AGREEMENT, RabbitConstant.RoutingKey.SIGN_RISK_TIPS_AGREEMENT, projectInvestOrder.getId());
			}
		}
		// 智能投风险提示书
		ProjectContract intelligenceRiskPromptContract = projectContractMapperExt.queryProjectContractByUserIdAndProjectIdAndContractType(packageInvestOrder.getUserId(),
				packageInvestOrder.getPackageId(), FadadaContractTemplateEnum.TemplateTypeEnum.INTELLIGENCE_RISK_PROMPT.getType());
		if (Objects.isNull(intelligenceRiskPromptContract)) {
			intelligenceRiskPromptContract = fadadaContractContext.initContractRecord(packageInvestOrder.getPackageId(), packageInvestOrder.getUserId(), "B_" + packageInvestOrder.getPackageId(),
					FadadaContractTemplateEnum.TemplateTypeEnum.INTELLIGENCE_RISK_PROMPT.getType(), TradeTypeEnum.INVEST.getType(), packageInvestOrder.getId());
			fadadaContractContext.saveProjectContract(intelligenceRiskPromptContract);
		}
		if (Objects.isNull(intelligenceRiskPromptContract.getViewUrl()) || Objects.isNull(intelligenceRiskPromptContract.getDownloadUrl())) {
			ProjectContract risk = intelligenceRiskPrompt.contractHandler(FadadaContractTemplateEnum.TemplateTypeEnum.INTELLIGENCE_RISK_PROMPT, packageInvestOrder, packageInvestOrder.getUserId());
			risk.setId(intelligenceRiskPromptContract.getId());
			fadadaContractContext.updateProjectContract(risk);
		}
		// 智能投服务书
		ProjectContract intelligenceServiceProtocolContract = projectContractMapperExt.queryProjectContractByUserIdAndProjectIdAndContractType(packageInvestOrder.getUserId(),
				packageInvestOrder.getPackageId(), FadadaContractTemplateEnum.TemplateTypeEnum.INTELLIGENT_SERVICE_PROTOCOL.getType());
		if (Objects.isNull(intelligenceServiceProtocolContract)) {
			intelligenceServiceProtocolContract = fadadaContractContext.initContractRecord(packageInvestOrder.getPackageId(), packageInvestOrder.getUserId(), "B_" + packageInvestOrder.getPackageId(),
					FadadaContractTemplateEnum.TemplateTypeEnum.INTELLIGENT_SERVICE_PROTOCOL.getType(), TradeTypeEnum.INVEST.getType(), packageInvestOrder.getId());
			fadadaContractContext.saveProjectContract(intelligenceServiceProtocolContract);
		}
		if (Objects.isNull(intelligenceServiceProtocolContract.getViewUrl()) || Objects.isNull(intelligenceServiceProtocolContract.getDownloadUrl())) {
			ProjectContract serviceProtocol = intelligentServiceProtocol.contractHandler(FadadaContractTemplateEnum.TemplateTypeEnum.INTELLIGENT_SERVICE_PROTOCOL, packageInvestOrder,
					packageInvestOrder.getUserId());
			serviceProtocol.setId(intelligenceServiceProtocolContract.getId());
			fadadaContractContext.updateProjectContract(serviceProtocol);
		}
	}

	/**
	 * 智能投投资结束发送消息
	 * 
	 * @param projectPackageInvestOrderId
	 */
	@Transactional(isolation = READ_COMMITTED)
	public void sendInvestEndMsg(Long projectPackageInvestOrderId) {
		log.info("智能投投资终止状态接受订单号{}", projectPackageInvestOrderId);
		DecimalFormat df = new DecimalFormat("#,##0.00");
		ProjectPackageInvestOrder packageInvestOrder = projectPackageInvestOrderMapperExt.selectByPrimaryKey(projectPackageInvestOrderId);
		String userId = packageInvestOrder.getUserId();
		if (PackageInvestOrderEvolutionStatusEnum.CLOSED.getValue().equals(packageInvestOrder.getEvolutionStatus())) {
			sendIntelligenceMsg(userId, LetterTemplateKeyEnum.INTELLIGENCE_INVEST_FAILURE, JPushTemplateKeyEnum.INTELLIGENCE_INVEST_FAILURE, SMSTemplateKeyEnum.INTELLIGENCE_INVEST_FAILURE.getKey(),
					packageInvestOrder.getTitle(), packageInvestOrder.getDeadline());
			log.info("智能投投资终止状态接受订单号:{},状态关闭", projectPackageInvestOrderId);
		} else if (packageInvestOrder.getAmount().compareTo(packageInvestOrder.getRealInvestAmount()) == 0) {
			sendIntelligenceMsg(userId, LetterTemplateKeyEnum.INTELLIGENCE_INVEST_SUCCESS, JPushTemplateKeyEnum.INTELLIGENCE_INVEST_SUCCESS, SMSTemplateKeyEnum.INTELLIGENCE_INVEST_SUCCESS.getKey(),
					packageInvestOrder.getTitle(), packageInvestOrder.getDeadline(), df.format(packageInvestOrder.getAmount()));
			log.info("智能投投资终止状态接受订单号:{},状态成功", projectPackageInvestOrderId);
		} else {
			sendIntelligenceMsg(userId, LetterTemplateKeyEnum.INTELLIGENCE_INVEST_PARTIAL_SUCCESS, JPushTemplateKeyEnum.INTELLIGENCE_INVEST_PARTIAL_SUCCESS,
					SMSTemplateKeyEnum.INTELLIGENCE_INVEST_PARTIAL_SUCCESS.getKey(), packageInvestOrder.getTitle(), packageInvestOrder.getDeadline(), df.format(packageInvestOrder.getAmount()),
					df.format(packageInvestOrder.getRealInvestAmount()), df.format(packageInvestOrder.getAmount().subtract(packageInvestOrder.getRealInvestAmount())));
			log.info("智能投投资终止状态接受订单号:{},部分成功", projectPackageInvestOrderId);
		}
	}

	/**
	 * 发送信息 短信+通知+jpush
	 * 
	 * @param userId
	 * @param letter
	 * @param jpush
	 * @param smsKey
	 * @param param
	 */
	private void sendIntelligenceMsg(String userId, LetterTemplateKeyEnum letter, JPushTemplateKeyEnum jpush, String smsKey, Object... param) {
		List<Object> listParam = Lists.newArrayList(param);
		// 消息 通知
		UserLetterRequestDTO req = new UserLetterRequestDTO();
		req.setToUserId(userId);
		req.setUserLetterTypeEnum(UserLetterTypeEnum.LETTER_AND_JPUSH); // 通知+推送
		req.setLetterTemplateEnum(letter);
		req.setJpushTemplateKeyEnum(jpush);
		req.setLetterTemplateParamList(listParam);
		req.setJpushTemplateParamList(listParam);
		RpcContext.getContext().asyncCall(() -> userLetterDubboService.pushLetter(req));
		// 短信
		sendSmsDubboService.sendSmsByUserId(userId, smsKey, listParam);

	}

	/**
	 * 异常订单进行资金解冻
	 */
	public void unfreeAmount() {
		List<InvestAmountUnfreeze> result = investAmountUnfreezeMapperExt.selectNeedUnfreeze();
		if (CollectionUtils.isEmpty(result)) {
			return;
		}
		for (InvestAmountUnfreeze investAmountUnfreeze : result) {
			log.info("智能投定时任务解冻，订单号{}", investAmountUnfreeze.getId());
			try {
				niiwooBillDubboService.billIntelligenceInvestCancel(investAmountUnfreeze.getUserId(), investAmountUnfreeze.getId() + "");
				intelligenceOrderService.unfreezeAmountsuccess(investAmountUnfreeze.getId());
			} catch (Exception e) {
				log.warn("智能投定时任务解冻失败，订单号{}", investAmountUnfreeze.getId());
			}

		}

	}

	/**
	 * 修改智能投状态
	 * 
	 * @return
	 */
	@Transactional(isolation = READ_COMMITTED)
	public void changProjectPackageStatusByProjectId(Long projectId) {
		Long projectPackageId = projectPackageDetailMapperExt.selectPackageIdByProjectId(projectId);
		changProjectPackageStatus(projectPackageId);
	}

	/**
	 * 修改智能投状态
	 * 
	 * @return
	 */
	@Transactional(isolation = READ_COMMITTED)
	public void changProjectPackageStatus(Long projectPackageId) {
		ProjectPackage projectPackage = projectPackageMapperExt.selectByPrimaryKeyForUpdate(projectPackageId);
		// 默认流包，最低级流包，只有放款中和流标的，则状态为售罄，存在投资中的标。则包为投资中
		Byte status = ProjectPackageStatusEnum.MISCARRY.getStatus();
		ProjectPackage newProjectPackage = new ProjectPackage();
		// 投资金额=0 流包
		if (BigDecimal.ZERO.compareTo(projectPackage.getAmount()) != 0) {
			// 打包中，待发布继续使用原始状态
			if (ProjectPackageStatusEnum.PACKAGING.getStatus().equals(projectPackage.getStatus()) || ProjectPackageStatusEnum.WAITING_PUBLISH.getStatus().equals(projectPackage.getStatus())) {
				status = projectPackage.getStatus();
			} else {
				List<Long> projectIds = projectPackageDetailMapperExt.selectProjectIdByPackageId(projectPackageId);
				List<Project> projects = projectMapperExt.queryProjectListByProjectIds(projectIds);
				for (Project project : projects) {
					// 如果还存在投资中的标，则包状态为投资中
					if (ProjectSubStatusEnum.COLLECTING_INIT.getSubStatus() == project.getSubStatus()) {
						status = ProjectPackageStatusEnum.INVESTING.getStatus();
						break;
						// 标的处于还款中、已结清或者放款中 则状态默认售罄
					} else if (ProjectStatusEnum.REPAYING.getStatus().equals(project.getStatus()) || ProjectSubStatusEnum.COLLECTING_LENDING.getSubStatus() == project.getSubStatus()
							|| ProjectStatusEnum.FINISH.getStatus().equals(project.getStatus())) {
						status = ProjectPackageStatusEnum.SALE_OUT.getStatus();
					}
				}
				// 当包的状态不是售罄，但是投资金额=包的金额状态为 已抢光
				if (!ProjectPackageStatusEnum.SALE_OUT.getStatus().equals(status) && projectPackage.getAmount().equals(projectPackage.getInvestedAmount())) {
					status = ProjectPackageStatusEnum.PRESALE_OUT.getStatus();
				}
			}
		}
		newProjectPackage.setPackageId(projectPackageId);
		newProjectPackage.setStatus(status);
		newProjectPackage.setUpdateTime(new Date());
		projectPackageMapperExt.updateByPrimaryKeySelective(newProjectPackage);
	}
	/**
	 * 薪金宝获取智能投期限配置
	 * @return
	 */
	public SelectIntelligenceDeadlineResponseDTO selectIntelligenceDeadline(SelectIntelligenceDeadlineRequestDTO requestDto) {
		Map<String,String> mapResult = tradeConfigLocalService.selectTradeConfigByEmuns(TradeConfigEnum.SALARY_BAO_INVESTMENT_TAB1,TradeConfigEnum.SALARY_BAO_INVESTMENT_TAB2,TradeConfigEnum.SALARY_BAO_INVESTMENT_TAB3);
		
		SelectIntelligenceDeadlineResponseDTO deadlineResult = new SelectIntelligenceDeadlineResponseDTO();
		if(Objects.isNull(mapResult)) {
			return deadlineResult;
		}
		List<DeadlineDTO> selector = new ArrayList<>();
		convertResult(selector,mapResult.get(TradeConfigEnum.SALARY_BAO_INVESTMENT_TAB1.getConfigKey())); 
		convertResult(selector,mapResult.get(TradeConfigEnum.SALARY_BAO_INVESTMENT_TAB2.getConfigKey())); 
		convertResult(selector,mapResult.get(TradeConfigEnum.SALARY_BAO_INVESTMENT_TAB3.getConfigKey())); 
		deadlineResult.setSelector(selector);
		Set<String> deadlineExsitSet = new HashSet<>();
 		for (DeadlineDTO deadlineDTO : selector) {
 			deadlineExsitSet.add(deadlineDTO.getDeadline()+"|"+deadlineDTO.getDeadlineUnit());
		}
		IntelligenceInvestPageRequestDTO requestParam = new IntelligenceInvestPageRequestDTO();
		requestParam.setOffset(0);
		requestParam.setPackageChannelCode(requestDto.getChannelCode());
		// 可投状态下
		List<ProjectPackage> list = projectPackageMapperExt.getIntelligenceInvestList(requestParam);
		if(!CollectionUtils.isEmpty(list)) {
			List<DeadlineDTO> existSelector = new ArrayList<>();
			Set<String> deadlineSet = new  HashSet<>();
			for (ProjectPackage deadlineDTO : list) {
				String deadlineName = deadlineDTO.getDeadline()+"|"+deadlineDTO.getDeadlineUnit();
				if(deadlineExsitSet.contains(deadlineName)) {
					deadlineSet.add(deadlineName);
				}
			}
			for (String value : deadlineSet) {
				convertResult(existSelector,value);
			}
			deadlineResult.setExistSelector(existSelector);
		}
		
		return deadlineResult;
	}
	/**
	 * 转换生成期限类型
	 * @param selector
	 * @param value
	 */
	private void convertResult(List<DeadlineDTO> selector, String value) {
		if(StringUtils.isBlank(value)) {
			return ;
		}
		String[] vs = value.trim().split("\\|");
		DeadlineDTO deadlineDTO = new DeadlineDTO();
		deadlineDTO.setDeadline(Integer.valueOf(vs[0].trim()));
		deadlineDTO.setDeadlineUnit(Byte.valueOf(vs[1].trim()));
		selector.add(deadlineDTO);
	}


	/**
	 * 是否可建群
	 *
	 * @param projectType
	 * @param investorType
	 * @param orgId
	 * @return
	 */
	private boolean enableAddGroup(Byte projectType, Byte investorType, String orgId){
		// 普通投资
		if(Byte.valueOf("1").equals(investorType)){ // 个人投资人
			if(!ProjectTypeEnum.ORG_SPEEDLOAN_NEW.getValue().equals(projectType)// 非机构标或
					|| (ProjectTypeEnum.ORG_SPEEDLOAN_NEW.getValue().equals(projectType)) && OrgConstant.NIWOTA_ORG_ID.equals(orgId)){ // 机构标中的你我他机构(暖心借标的)
				return true;
			}
		}
		return false;
	}
	/**
	 * 不同身份（新手，非新手，看到的智能投的可投数量不一样，需要带入这个进行查询）
	 * @param userIdentity
	 * @param code
	 * @return
	 */
	public IsSmartInvestSoldOutResponseDTO queryIntelligenceInvestCount(Byte userIdentity, Byte code) {
		IsSmartInvestSoldOutResponseDTO dto = new IsSmartInvestSoldOutResponseDTO();
		String configValue = tradeConfigLocalService.selectTradeConfigByEmun(TradeConfigEnum.SMART_INVESTMENT_SHOW_SWITCH);
		if (StringUtils.isNotBlank(configValue) && "1".equals(configValue)) {
			Integer count = projectPackageMapperExt.queryIntelligenceInvestCount(userIdentity, code);
			dto.setIsSmartInvestSoldOut(count > 0 ? 0 : 1);
		}
		int totalCount = projectInvestingMapperExt.accountFanLiInvestPageByParams(ChannelCodeEnum.APP.getChanelCode());
		dto.setIsProjectInvestSoldOut(totalCount > 0 ? 0 : 1);
		return dto;
	}

}
